﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using TireSizesCalculator.Model;
using TireSizesCalculator.Controller;

namespace TireSizesCalculator.View
{
    public partial class TireGraphicPanel : UserControl
    {
        private static readonly string RADIUS_FORMAT = "{0}\'";
        private SolidBrush _brushHole;
        private SolidBrush _brushFront;
        private SolidBrush _brushSide;
        private SolidBrush _brushText;
        private Pen _penLine;
        private Pen _penHelpLine;

        private TireSizeController _controller;
        public TireSizeController Controller
        {
            get
            {
                return _controller;
            }
            set
            {
                _controller = value;

                SizesChange();
            }
        }
        private double _maxTireSize;
        private double _minTireSize;
        private int _minSize = 131;      // do ustawienia później
        private int _maxSize;
        private int _padding = 5;
        private float _ellipseWidth = 60f;
        private double _unit;
        private readonly double trigonometric45 = Math.Sin(Math.PI / 4d);
        private TireSize _tire;
        public TireSize TireMain
        {
            get
            {
                return _tire;
            }
            set
            {
                _tire = value;
                Refresh();
            }
        }
        private TireSize _tireRight;
        public TireSize TireRight
        {
            get
            {
                return _tireRight;
            }
            set
            {
                _tireRight = value;
                Refresh();
            }
        }



        public Color TireFrontColor
        {
            get
            {
                return _brushFront.Color;
            }
            set
            {
                _brushFront = new SolidBrush(value);
                Refresh();
            }
        }

        public Color TireSideColor
        {
            get
            {
                return _brushSide.Color;
            }
            set
            {
                _brushSide = new SolidBrush(value);
                Refresh();
            }
        }

        public Color ArrowLineColor
        {
            get
            {
                return _penLine.Color;
            }
            set
            {
                _penLine = new Pen(new SolidBrush(value));
                Refresh();
            }
        }

        public Color LineColor
        {
            get
            {
                return _penHelpLine.Color;
            }
            set
            {
                _penHelpLine = new Pen(new SolidBrush(value));
                Refresh();
            }
        }

        public Color TextColor
        {
            get
            {
                return _brushText.Color;
            }
            set
            {
                _brushText = new SolidBrush(value);
                Refresh();
            }
        }



        public TireGraphicPanel()
        {
            InitializeComponent();
            DoubleBuffered = true;
            _brushHole = new SolidBrush(DefaultBackColor);

            TireFrontColor = Color.Gray;
            TireSideColor = Color.DarkSlateGray;
            ArrowLineColor = Color.DarkSlateGray;
            LineColor = Color.Red;
            TextColor = Color.DarkSlateGray;

            BackColorChanged += new EventHandler(BackgroundChanged);

            _maxSize = Height - 2 * _padding;
            TireMain = new TireSize(165, 65, 15);
        }


        private void SizesChange()
        {
            if (_controller == null)
            {
                return;
            }

            _minTireSize = Controller.GetMinTireSize();
            _maxTireSize = Controller.GetMaxTireSize();
            _maxSize = Height - 2 * _padding;

            var diff1 = _maxSize - _minSize;
            var diff2 = _maxTireSize - _minTireSize;
            _unit = 0d;
            if (_maxTireSize != _minTireSize)
            {
                _unit = (double)diff1 / diff2;
            }
        }

        private int CalcHeight(double tireHeight)
        {
            if (_unit != 0)
            {
                return (int)(_minSize + _unit * (tireHeight - _minTireSize));
            }

            return _maxSize;
        }

        private void BackgroundChanged(object sender, EventArgs e)
        {
            _brushHole = new SolidBrush(BackColor);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            SizesChange();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics graph = e.Graphics;

            if (TireMain != null && TireRight != null)
            {
                var width1 = 47f + TireMain.Width / 4 + _ellipseWidth;
                var width2 = 47f + TireRight.Width / 4 + _ellipseWidth;
                var wholeWidth = width1 + width2 + 20f;
                var x1 = (Width - wholeWidth) / 2f;
                x1 += 47f;
                var x2 = (x1 + wholeWidth) - (width2);
                x2 += 47f;
                DrawTire(graph, TireMain, x1);
                DrawTire(graph, TireRight, x2);
            }
            else if (TireMain != null)
            {
                var x = (Width - (47f + TireMain.Width / 4 + _ellipseWidth)) / 2f;
                x += 47f;
                DrawTire(graph, TireMain, x);
            }
        }

        private void DrawTire(Graphics graph, TireSize tire, float x)
        {
            var tireWidth = tire.Width / 4;
            var radius = CalcHeight(tire.RadiusInMiliMeters) - 31f;
            var tireHeightMM = (tire.Width * (tire.Profile / 100f));
            var tireHeight = tireHeightMM / 5;

            var w = 47f + tireWidth + _ellipseWidth;
            var h = 31f + radius;

            var y = (Height - h) / 2f;
            y += 31f;
            var middleX = x + (_ellipseWidth / 2);


            graph.FillEllipse(_brushFront, x + tireWidth, y, _ellipseWidth, radius);
            graph.FillRectangle(_brushFront, middleX, y, tireWidth, radius);

            graph.FillEllipse(_brushSide, x, y, _ellipseWidth, radius);

            graph.FillEllipse(_brushHole, x + (tireHeight / 2), y + tireHeight, _ellipseWidth - tireHeight, radius - 2 * tireHeight);

            var x2 = x - 30f;
            var y2 = y + radius;
            var x3 = x - 10f;
            var y3 = y + tireHeight;
            var x4 = middleX + tireWidth;
            var y4 = y - 15;

            DrawDashedLine(graph, _penHelpLine, x2 - 10, y, middleX, y);
            DrawDashedLine(graph, _penHelpLine, x3 - 10, y3, middleX, y3);
            DrawDashedLine(graph, _penHelpLine, x2 - 10, y2, middleX, y2);
            DrawDashedLine(graph, _penHelpLine, middleX, y4, middleX, y);
            DrawDashedLine(graph, _penHelpLine, x4, y4, x4, y);

            DrawArrows(graph, x2, y, x2, y2);
            DrawArrows(graph, x3, y, x3, y3);
            DrawArrows(graph, middleX, y4, x4, y4);

            graph.RotateTransform(-90f);
            graph.DrawString(string.Format(RADIUS_FORMAT, TireMain.Radius), SystemFonts.MenuFont, _brushText, -(y + Math.Abs(y2 - y) / 2 + 9), x2 - 17);
            graph.DrawString(tireHeightMM.ToString(), SystemFonts.MenuFont, _brushText, -(y + Math.Abs(y3 - y) / 2 + 9), x3 - 17);
            graph.ResetTransform();
            graph.DrawString(TireMain.Width.ToString(), SystemFonts.MenuFont, _brushText, middleX + Math.Abs(x4 - middleX) / 2 - 9, y4 - 16);
        }

        private void DrawArrows(Graphics graph, float x1, float y1, float x2, float y2)
        {
            int arrowLength = 3;
            DrawDashedLine(graph, _penLine, x1, y1, x2, y2);

            if (y1 == y2)
            {
                graph.DrawLine(_penLine, x1, y1, x1 + arrowLength, y1 - arrowLength);
                graph.DrawLine(_penLine, x1, y1, x1 + arrowLength, y1 + arrowLength);
                graph.DrawLine(_penLine, x2, y2, x2 - arrowLength, y2 - arrowLength);
                graph.DrawLine(_penLine, x2, y2, x2 - arrowLength, y2 + arrowLength);
            }
            else if (x1 == x2)
            {
                graph.DrawLine(_penLine, x1, y1, x1 - arrowLength, y1 + arrowLength);
                graph.DrawLine(_penLine, x1, y1, x1 + arrowLength, y1 + arrowLength);
                graph.DrawLine(_penLine, x2, y2, x2 - arrowLength, y2 - arrowLength);
                graph.DrawLine(_penLine, x2, y2, x2 + arrowLength, y2 - arrowLength);
            }


        }

        private void DrawDashedLine(Graphics graph, Pen pen, float x1, float y1, float x2, float y2, float dashLength = 2f)
        {
            float lineLength = (float)Math.Sqrt(Math.Pow(x1 - x2, 2d) + Math.Pow(y1 - y2, 2d));
            float count = lineLength / (dashLength * 2);
            float dx = Math.Abs(x1 - x2) / (count * 2);
            if (x1 > x2)
            {
                dx = -dx;
            }
            float dy = Math.Abs(y1 - y2) / (count * 2);
            if (y1 > y2)
            {
                dy = -dy;
            }

            float drawnLength = 0f;
            float drawnX = 0f;
            float drawnY = 0f;

            while (drawnLength < lineLength)
            {
                graph.DrawLine(pen, x1 + drawnX, y1 + drawnY, x1 + (drawnX + dx), y1 + (drawnY + dy));
                drawnLength += 2 * dashLength;
                drawnX += 2 * dx;
                drawnY += 2 * dy;
            }
        }
    }

}
